// OpentTxl-C Version 11 bootstrap
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// This is the bootstrap of the parser for the TXL Source Language itself, used to parse TXL programs.
// This module builds the grammar tree for the TXL language from the pre-scanned version of the TXL grammar 
// (boot.i) automatically generated from the TXL boostrap grammar (bootstrap/Txl-11-boostrap.grm). 

// Modification Log

// v11.0 Initial revision, adapted from OpenTxl 11.0

#include "support.h"
#include "limits.h"
#include "tokens.h"
#include "trees.h"
#include "idents.h"
#include "errors.h"

// Insure consistency with interface
#include "boot.h"

// Automatically generated pre-scanned encoding of the TXL language grammar
#include "bootgrm.h"

static int bootstrap_bootstrapToken;
static string bootstrap_nextToken;

static treePT bootstrap_bootSymbols[maxBootstrapSymbols];
static int bootstrap_nBootSymbols = 0;

// Bootstrap routines
static int bootstrap_enterBootSymbol (const tokenT partId, const enum treeKindT kind)
{
    for (int s = 1; s <= bootstrap_nBootSymbols; s++) {
        if ((tree_trees[bootstrap_bootSymbols[s]].name) == partId) {
            return (s);
        }
    }

    if (bootstrap_nBootSymbols == maxBootstrapSymbols) {
        error ("TXL bootstrap", "Too many symbols in TXL bootstrap", INTERNAL_FATAL, 931);
    }

    bootstrap_nBootSymbols++;
    bootstrap_bootSymbols[bootstrap_nBootSymbols] = tree_newTreeInit (kind, partId, partId, 0, nilKid);
    return (bootstrap_nBootSymbols);
}

static void bootstrap_getNextToken (const string expectedToken)
{
    bootstrap_bootstrapToken++;
    stringcpy (bootstrap_nextToken, bootstrap_bootstrapStrings[bootstrap_bootstrapTokens[bootstrap_bootstrapToken]]);
    if ((stringcmp (expectedToken, "") != 0) && (stringcmp (bootstrap_nextToken, expectedToken) != 0)) {
        string message;
        stringprintf (message, "Syntax error in TXL bootstrap - expected '%s' + got '%s'", 
            expectedToken, bootstrap_nextToken);
        error ("TXL bootstrap", message, INTERNAL_FATAL, 932);
    }
}

static void bootstrap_processDefineBody (const int parentIndex)
{
    // it's an order tree until we see otherwise
    tree_setKind (bootstrap_bootSymbols[parentIndex], treeKind_order);

    // allocate first kid
    int nKids = 0;
    kidPT kidListKP = tree_newKid ();
    tree_setKids (bootstrap_bootSymbols[parentIndex], kidListKP);

    bootstrap_getNextToken ("");

    while (true) {
        treePT  kidTP;

        if (stringcmp (bootstrap_nextToken, "[") == 0) {
            // non-terminal or builtin
            bootstrap_getNextToken ("");
            tokenT identIndex = ident_install (bootstrap_nextToken, treeKind_id);
            const int kidIndex = bootstrap_enterBootSymbol (identIndex, treeKind_id);
            kidTP = bootstrap_bootSymbols[kidIndex];
            bootstrap_getNextToken ("]");

        } else {
            // literal
            if (stringcmp (bootstrap_nextToken, "'") == 0) {
                bootstrap_getNextToken ("");
            }
            const tokenT identIndex = ident_install (bootstrap_nextToken, treeKind_id);
            kidTP = tree_newTreeInit (treeKind_literal, identIndex, identIndex, 0, nilKid);
        }

        nKids++;

        tree_setKidTree (kidListKP, kidTP);
        bootstrap_getNextToken ("");

        if (stringcmp (bootstrap_nextToken, "end") == 0) break;

        if ((tree_trees[bootstrap_bootSymbols[parentIndex]].kind) == treeKind_choose) {
            if (stringcmp (bootstrap_nextToken, "|") == 0) {
                bootstrap_getNextToken ("");
            } else {
                string message;
                stringprintf (message, "Syntax error in TXL bootstrap - expected '|', got '%s'", bootstrap_nextToken);
                error ("TXL bootstrap", message, INTERNAL_FATAL, 933);
            }

        } else if (stringcmp (bootstrap_nextToken, "|") == 0) {
            if (nKids == 1) {
                tree_setKind (bootstrap_bootSymbols[parentIndex], treeKind_choose);
                bootstrap_getNextToken ("");
            } else {
                error ("TXL bootstrap", "Syntax error in TXL bootstrap - multiple tokens in choice alternative", INTERNAL_FATAL, 934);
            }
        }

        // allocate next kid
        kidListKP = tree_newKid ();
    }

    // necessary for high-falutin' parser optimizations!
    tree_setCount (bootstrap_bootSymbols[parentIndex], nKids);
}

static void bootstrap_processDefine (void) {
    bootstrap_getNextToken ("define");

    //get name of production
    bootstrap_getNextToken ("");

    const tokenT identIndex = ident_install (bootstrap_nextToken, treeKind_id);
    int symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_id);

    bootstrap_processDefineBody (symbolIndex);

    assert (stringcmp (bootstrap_nextToken, "end") == 0);
    bootstrap_getNextToken ("define");
}

static void bootstrap_setUpBuiltins (void) {
    tokenT identIndex;
    int symbolIndex;
    identIndex = ident_install ("stringlit", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_stringlit);
    identIndex = ident_install ("charlit", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_charlit);
    identIndex = ident_install ("number", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_number);
    identIndex = ident_install ("id", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_id);
    identIndex = ident_install ("token", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_token);
    identIndex = ident_install ("key", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_key);
    identIndex = ident_install ("empty", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_empty);
    identIndex = ident_install ("KEEP", treeKind_id);
    symbolIndex = bootstrap_enterBootSymbol (identIndex, treeKind_empty);
}

void bootstrap_makeGrammarTree (treePT *grammarTreeTP)
{
    bootstrap_bootstrapToken = 0;
    bootstrap_nBootSymbols = 0;

    // TXL grammar tree root
    const tokenT programT = ident_install ("program", treeKind_id);
    const int programIndex = bootstrap_enterBootSymbol (programT, treeKind_id);

    *grammarTreeTP = bootstrap_bootSymbols[programIndex];

    // Install the TXL built-in symbols
    bootstrap_setUpBuiltins ();

    // Defines of the TXL grammar
    while (true) {
        bootstrap_processDefine ();
        if (bootstrap_bootstrapToken == bootstrap_numBootstrapTokens) break;
    }

    // Check that there are no undefined symbols
    int errorcount = 0;

    for (int i = 1; i <= bootstrap_nBootSymbols; i++) {
        if ((tree_trees[bootstrap_bootSymbols[i]].kind) == treeKind_undefined) {
            string message;
            stringprintf (message, "[%s] has not been defined", 
                *ident_idents[tree_trees[bootstrap_bootSymbols[i]].name]);
            error ("TXL bootstrap", message, INTERNAL_FATAL, 935);
            errorcount++;
        }
    }

    if (errorcount != 0) {
        throw (QUIT);
    }
}
